﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace jedlik
{
    public static class Debug
    {
        public static string FormattedTime
        {
            get { return "[" + DateTime.Now.Hour + ":" + DateTime.Now.Minute + ":" + DateTime.Now.Second + "." + DateTime.Now.Millisecond + "]"; }
        }
        public static string FormattedDate
        {
            get { return "(" + DateTime.Now.Year + "/" + DateTime.Now.Month + "/" + DateTime.Now.Day + ")"; }
        }
        public static string LogStamp
        {
            get { return FormattedDate + FormattedTime; }
        }

        /// <summary>
        /// Print a debug message to the console with timestamp.
        /// </summary>
        /// <param name="type">The type of the message.</param>
        /// <param name="message">The message itself.</param>
        /// <param name="sender">The sender object. In most cases simply use the 'this' keyword.</param>
        public static void Message(DebugType type, string message, object sender, bool logIt = false)
        {
            string dateString = FormattedTime;
            string typeString = "(" + DebugTypeString(type) + ")";
            Console.WriteLine(typeString + dateString + message + " (by \'" + ((sender == null) ? "null" : sender.ToString()) + "\')");
            if (logIt)
                Logger.GetLogger().LogLine(message);
        }
        /// <summary>
        /// internal function
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        private static string DebugTypeString(DebugType t)
        {
            switch (t)
            {
                case DebugType.Information:
                    return "INF";
                case DebugType.Warning:
                    return "WRN";
                case DebugType.Fatal:
                    return "FTL";
                case DebugType.Unknown:
                    return "UNK";
                default:
                    return "wut?";
            }
        }

        public sealed class Logger
        {
            private FileStream fs;
            private StreamWriter ws;
            private static Logger instance;
            private bool _broken = false;

            private Logger()
            {
                Debug.Message(DebugType.Information, "Attemping to open and/or create log file", this);
                try
                {
                    fs = new FileStream("debug.log", FileMode.Append);
                    ws = new StreamWriter(fs);
                    ws.AutoFlush = true;
                }
                catch (Exception e)
                {
                    Debug.Message(DebugType.Warning, "Error opening/creating log file! Logging disabled for this session. Error message: " + e.Message, this);
                    _broken = true;
                    ws.Close();
                    fs.Close();
                    ws = null;
                    fs = null;
                }
                if (fs != null && ws != null && fs.CanWrite && !_broken)
                {
                    ws.WriteLine("Jedlik init log at " + FormattedDate + FormattedTime);
                    ws.WriteLine(LogStamp + "Log init finished!");
                    ws.WriteLine(LogStamp + "Allocated memory: " + GC.GetTotalMemory(false) + " bytes");
                }
            }
            /// <summary>
            /// Singleton-alike patterned logger class.  
            /// </summary>
            /// <returns></returns>
            public static Logger GetLogger()
            {
                if (instance == null)
                    instance = new Logger();
                return instance;
            }
            /// <summary>
            /// Write the specified line to the log file.
            /// </summary>
            /// <param name="line">The line to write</param>
            public void LogLine(string line)
            {
                if (_broken)
                    return;
                try
                {
                    ws.WriteLine(LogStamp + line);
                }
                catch (NullReferenceException e)
                {
                    Message(DebugType.Warning, "Although the log seemed to be writable, it's not? One of the writer objects (FileStream or StreamWriter) are null. Error message: " + e.Message, this);
                }
                catch (Exception e)
                {
                    Message(DebugType.Warning, "An unknown error occured while attemping to write to the log. Error message: " + e.Message, this);
                }
                // We set the ws to autoflush, but just to make it sure...
                ws.Flush();
            }
            /// <summary>
            /// Finalize and close the log file. This is expected to be called only on shutdown, but the Log is re-accessible through the GetLogger() method any time.
            /// </summary>
            public static void FinalizeLog()
            {
                if (instance == null)
                {
                    Debug.Message(DebugType.Warning, "Log instance missing, but trying to finalize it?", null);
                }
                instance.LogLine("Closing log at " + LogStamp);
                instance.ws.Flush();
                instance.fs.Flush();
            }
            ~Logger()
            {
                //FinalizeLog();
            }
        }
    }
}
